package com.jisf.driver.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.jisf.driver.entity.enumtype.SubjectEnum;
import com.jisf.driver.entity.po.*;
import com.jisf.driver.entity.vo.ExamineStudentResponseVo;
import com.jisf.driver.exception.DrivingSchoolException;
import com.jisf.driver.exception.SystemException;
import com.jisf.driver.mapper.*;
import com.jisf.driver.response.ResultCode;
import com.jisf.driver.service.IStudentExamineService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jisf.driver.utils.LogUtil;
import com.jisf.driver.utils.UserUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.Date;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author jisf
 * @since 2022-05-10
 */
@Service
public class StudentExamineServiceImpl extends ServiceImpl<StudentExamineMapper, StudentExamine> implements IStudentExamineService {

    @Autowired
    private StudentExamineMapper studentExamineMapper;

    @Autowired
    private ExamineMapper examineMapper;

    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private StudentCoachMapper studentCoachMapper;

    @Autowired
    private CoachMapper coachMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private LogUtil log;

    /**
     * 考试报名
     *
     * @param id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResultCode appoint(Integer id) {
        try {
            Integer userid = UserUtil.userIdThreadLocal.get();
            // 判断是否报名人数已满
            Examine examine = examineMapper.selectById(id);
            Integer appointNumber = examine.getAppointNumber();
            Integer examNumber = examine.getExamNumber();

            // 存储日志
            String operator = UserUtil.usernameThreadLocal.get();
            String msg = String.format("用户名为%s的学员报名场次为%s的考试", operator, examine.getName());

            if (appointNumber >= examNumber){
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                return ResultCode.EXAMINE_HAVE_APPOINT_FAILED;
            }

            // 判断是否报名该考试
            QueryWrapper<StudentExamine> studentExamineQueryWrapper = new QueryWrapper<>();
            studentExamineQueryWrapper.eq("examine_id", id).eq("student_id", userid);
            int value = studentExamineMapper.selectCount(studentExamineQueryWrapper).intValue();
            if (value != 0) {
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                return ResultCode.EXAMINE_HAVE_APPOINT_FAILED;
            }


            SubjectEnum subject = examine.getSubject();
            // 判断该考生的该考试科目是否全部结束
            Integer examineAppoint = examineMapper.getExamineAppoint(userid, subject);
            if (examineAppoint != 0) {
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                return ResultCode.EXAMINE_HAVE_APPOINT_OTHER_FAILED;
            }

            // 剩余考试次数是否足够
            QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
            studentQueryWrapper.eq("userid", userid);
            Student student = studentMapper.selectOne(studentQueryWrapper);
            Integer subjectTime;
            switch (subject.getCode()) {
                case 1:
                    subjectTime = student.getSubjectOneExam();
                    student.setSubjectOneExam(subjectTime - 1);
                    break;
                case 2:
                    subjectTime = student.getSubjectTwoExam();
                    student.setSubjectTwoExam(subjectTime - 1);
                    break;
                case 3:
                    subjectTime = student.getSubjectThreeExam();
                    student.setSubjectThreeExam(subjectTime - 1);
                    break;
                case 4:
                    subjectTime = student.getSubjectFourExam();
                    student.setSubjectFourExam(subjectTime - 1);
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + subject);
            }
            if (subjectTime <= 0) {
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                return ResultCode.EXAMINE_APPOINT_SUBJECT_TIME_FAILED;
            }

            // 预约考试
            StudentExamine studentExamine = new StudentExamine();
            studentExamine.setExamineId(id);
            studentExamine.setStudentId(userid);
            int examineCount = studentExamineMapper.getExamineCount(userid, subject);
            studentExamine.setCount(examineCount + 1);
            int insert = studentExamineMapper.insert(studentExamine);
            // 更新考试报名人数
            examine.setAppointNumber(appointNumber + 1);
            int updateById = examineMapper.updateById(examine);
            //扣除考试次数
            int update = studentMapper.updateById(student);
            if (insert == 0 || update == 0 || updateById == 0) {
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return ResultCode.EXAMINE_APPOINT_FAILED;
            }
            String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, true));
            log.info(drivingSchoolLog);
            return ResultCode.EXAMINE_APPOINT_SUCCESS;
        } catch (DrivingSchoolException e) {
            throw new DrivingSchoolException(ResultCode.EXAMINE_APPOINT_FAILED);
        } catch (Exception e) {
            throw new SystemException(e);
        }
    }

    /**
     * 取消报名
     *
     * @param id
     * @return
     */
    @Transactional
    @Override
    public ResultCode cancelAppoint(Integer id) {
        try {
            //取消预约考试
            Integer userid = UserUtil.userIdThreadLocal.get();
            Examine examine = examineMapper.selectById(id);
            QueryWrapper<StudentExamine> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("examine_id", id).eq("student_id", userid);
            int value = studentExamineMapper.selectCount(queryWrapper).intValue();

            // 存储日志
            String operator = UserUtil.usernameThreadLocal.get();
            String msg = String.format("用户名为%s的学员取消了场次为%s的考试报名", operator, examine.getName());

            if (value == 0) {
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                return ResultCode.EXAMINE_HAVE_CANCEL_APPOINT_FAILED;
            }
            int delete = studentExamineMapper.delete(queryWrapper);

            // 返还考试次数

            SubjectEnum subject = examine.getSubject();
            QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
            studentQueryWrapper.eq("userid", userid);
            Student student = studentMapper.selectOne(studentQueryWrapper);
            Integer subjectTime;
            switch (subject.getCode()) {
                case 1:
                    subjectTime = student.getSubjectOneExam();
                    student.setSubjectOneExam(subjectTime + 1);
                    break;
                case 2:
                    subjectTime = student.getSubjectTwoExam();
                    student.setSubjectTwoExam(subjectTime + 1);
                    break;
                case 3:
                    subjectTime = student.getSubjectThreeExam();
                    student.setSubjectThreeExam(subjectTime + 1);
                    break;
                case 4:
                    subjectTime = student.getSubjectFourExam();
                    student.setSubjectFourExam(subjectTime + 1);
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + subject);
            }
            int update = studentMapper.updateById(student);

            //更新考试报名人数
            examine.setAppointNumber(examine.getAppointNumber() - 1);
            int updateById = examineMapper.updateById(examine);

            if (delete == 0 || update == 0 || updateById == 0) {
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return ResultCode.EXAMINE_CANCEL_APPOINT_FAILED;
            }
            String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, true));
            log.info(drivingSchoolLog);
            return ResultCode.EXAMINE_CANCEL_APPOINT_SUCCESS;
        } catch (DrivingSchoolException e) {
            throw new DrivingSchoolException(ResultCode.EXAMINE_CANCEL_APPOINT_FAILED);
        } catch (Exception e) {
            throw new SystemException(e);
        }
    }

    /**
     * 获取考试学生
     *
     * @param id
     * @return
     */
    @Override
    public List<ExamineStudentResponseVo> getExamineStudent(Integer id) {
        try {
            List<ExamineStudentResponseVo> examineStudent = studentExamineMapper.getExamineStudent(id);
            return examineStudent;
        } catch (Exception e) {
            throw new SystemException(e);
        }
    }

    /**
     * 为考生评分
     *
     * @param studentExamine
     * @return
     */
    @Transactional
    @Override
    public ResultCode setScore(StudentExamine studentExamine) {
        try {
            Integer examineId = studentExamine.getExamineId();
            Integer studentId = studentExamine.getStudentId();
            Integer grade = studentExamine.getGrade();
            UpdateWrapper<StudentExamine> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("examine_id", examineId)
                    .eq("student_id", studentId)
                    .set("grade", grade);
            int update = studentExamineMapper.update(null, updateWrapper);
            // 考试通过
            Integer count = studentExamine.getCount();
            if (count != null && count == 1) {
                Examine examine = examineMapper.selectById(examineId);
                SubjectEnum subject = examine.getSubject();
                if (subject.getCode() == 2) {
                    if (grade >= 80) {
                        // 设置教练的学生通过考试数量
                        setCoachStudentsPassNumber(studentId);
                        // 设置考试通过人数
                        examine.setPassNumber(examine.getPassNumber() + 1);
                        examineMapper.updateById(examine);
                    }
                } else {
                    if (grade >= 90) {
                        // 设置教练的学生通过考试数量
                        setCoachStudentsPassNumber(studentId);
                        // 设置考试通过人数
                        examine.setPassNumber(examine.getPassNumber() + 1);
                        examineMapper.updateById(examine);
                    }
                }
            }
            // 存储日志
            String operator = UserUtil.usernameThreadLocal.get();
            String username = userMapper.selectById(studentId).getUsername();
            String msg = String.format("用户名为%s的教练为用户名为%s的考生评分，成绩为%d", operator, username, grade);

            if (update != 0) {
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, true));
                log.info(drivingSchoolLog);
                return ResultCode.EXAMINE_SET_GRADE_SUCCESS;
            } else {
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                return ResultCode.EXAMINE_SET_GRADE_FAILED;
            }
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 设置教练的学生通过考试数量
     * @param studentId
     */
    private void setCoachStudentsPassNumber(Integer studentId) {
        try {
            QueryWrapper<StudentCoach> studentCoachQueryWrapper = new QueryWrapper<>();
            studentCoachQueryWrapper.eq("student_id", studentId);
            List<StudentCoach> studentCoaches = studentCoachMapper.selectList(studentCoachQueryWrapper);
            for (StudentCoach studentCoach : studentCoaches) {
                Integer coachId = studentCoach.getCoachId();
                QueryWrapper<Coach> coachQueryWrapper = new QueryWrapper<>();
                coachQueryWrapper.eq("userid", coachId);
                Coach coach = coachMapper.selectOne(coachQueryWrapper);
                coach.setStudentsPassNumber(coach.getStudentsPassNumber() + 1);
                coachMapper.updateById(coach);
            }
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 考试成绩备注
     * @param studentExamine
     * @return
     */
    @Override
    public ResultCode setRemark(StudentExamine studentExamine) {
        try {
            UpdateWrapper<StudentExamine> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("examine_id", studentExamine.getExamineId())
                    .eq("student_id", studentExamine.getStudentId())
                    .set("remark", studentExamine.getRemark());
            int update = studentExamineMapper.update(null, updateWrapper);
            return update != 0 ? ResultCode.EXAMINE_SET_REMARK_SUCCESS : ResultCode.EXAMINE_SET_REMARK_FAILED;
        }catch (Exception e){
            throw new SystemException(e);
        }
    }
}